/**
 ******************************************************************************
 * @file           : bsp_w25qxx.c
 * @brief          : W25Qxx SPI Flash Driver Implementation
 ******************************************************************************
 * @attention
 *
 * W25Qxx系列SPI Flash驱动程序实现文件
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./Devices/W25Qxx/bsp_w25qxx.h"
#include "./Devices/W25Qxx/bsp_w25qxx_config.h"

/* Private includes ----------------------------------------------------------*/
#include <string.h>

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define W25QXX_PAGE_SIZE        256
#define W25QXX_SECTOR_SIZE      4096
#define W25QXX_TIMEOUT_MS       1000

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static W25QXX_Info_t sg_w25qxx_info;

/* Private function prototypes -----------------------------------------------*/
static W25QXX_Error_e w25qxx_spi_transmit(const uint8_t *tx_data, uint16_t len);
static W25QXX_Error_e w25qxx_spi_receive(uint8_t *rx_data, uint16_t len);
static W25QXX_Error_e w25qxx_write_enable(void);
static W25QXX_Error_e w25qxx_wait_busy(void);
static uint8_t w25qxx_read_status_reg(uint8_t reg_num);

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       初始化W25Qxx
 */
void w25qxx_init(void)
{
    /* 初始化CS引脚 */
    W25QXX_CS_HIGH();
    
    /* 读取芯片ID */
    uint16_t manufacturer_id, device_id;
    if (w25qxx_read_id(&manufacturer_id, &device_id) == W25QXX_OK)
    {
        log_i("W25Qxx初始化成功 MID=0x%04X DID=0x%04X", manufacturer_id, device_id);
    }
    else
    {
        log_e("W25Qxx初始化失败");
    }
}

/**
 * @brief       读取芯片ID
 *
 * @param[out]  manufacturer_id: 厂商ID
 * @param[out]  device_id: 设备ID
 *
 * @retval      W25QXX_Error_e: 错误码
 */
W25QXX_Error_e w25qxx_read_id(uint16_t *manufacturer_id, uint16_t *device_id)
{
    uint8_t cmd[4] = {W25QXX_CMD_MANUFACTURER_ID, 0x00, 0x00, 0x00};
    uint8_t id[2];
    
    W25QXX_CS_LOW();
    w25qxx_spi_transmit(cmd, 4);
    w25qxx_spi_receive(id, 2);
    W25QXX_CS_HIGH();
    
    *manufacturer_id = id[0];
    *device_id = id[1];
    
    return W25QXX_OK;
}

/**
 * @brief       读取数据
 *
 * @param[in]   addr: 起始地址
 * @param[out]  buf: 数据缓冲区
 * @param[in]   len: 读取长度
 *
 * @retval      W25QXX_Error_e: 错误码
 */
W25QXX_Error_e w25qxx_read(uint32_t addr, uint8_t *buf, uint32_t len)
{
    uint8_t cmd[4];
    
    if (buf == NULL || len == 0)
    {
        return W25QXX_ERROR_PARAM;
    }
    
    cmd[0] = W25QXX_CMD_READ_DATA;
    cmd[1] = (addr >> 16) & 0xFF;
    cmd[2] = (addr >> 8) & 0xFF;
    cmd[3] = addr & 0xFF;
    
    W25QXX_CS_LOW();
    w25qxx_spi_transmit(cmd, 4);
    w25qxx_spi_receive(buf, len);
    W25QXX_CS_HIGH();
    
    return W25QXX_OK;
}

/**
 * @brief       写入数据
 *
 * @param[in]   addr: 起始地址
 * @param[in]   buf: 数据缓冲区
 * @param[in]   len: 写入长度
 *
 * @retval      W25QXX_Error_e: 错误码
 */
W25QXX_Error_e w25qxx_write(uint32_t addr, const uint8_t *buf, uint32_t len)
{
    uint32_t page_remain, write_len;
    uint8_t cmd[4];
    
    if (buf == NULL || len == 0)
    {
        return W25QXX_ERROR_PARAM;
    }
    
    while (len > 0)
    {
        /* 计算当前页剩余空间 */
        page_remain = W25QXX_PAGE_SIZE - (addr % W25QXX_PAGE_SIZE);
        write_len = (len < page_remain) ? len : page_remain;
        
        /* 写使能 */
        w25qxx_write_enable();
        
        /* 页编程 */
        cmd[0] = W25QXX_CMD_PAGE_PROGRAM;
        cmd[1] = (addr >> 16) & 0xFF;
        cmd[2] = (addr >> 8) & 0xFF;
        cmd[3] = addr & 0xFF;
        
        W25QXX_CS_LOW();
        w25qxx_spi_transmit(cmd, 4);
        w25qxx_spi_transmit(buf, write_len);
        W25QXX_CS_HIGH();
        
        /* 等待写入完成 */
        w25qxx_wait_busy();
        
        /* 更新地址和长度 */
        addr += write_len;
        buf += write_len;
        len -= write_len;
    }
    
    return W25QXX_OK;
}

/**
 * @brief       擦除扇区（4KB）
 *
 * @param[in]   sector_addr: 扇区地址
 *
 * @retval      W25QXX_Error_e: 错误码
 */
W25QXX_Error_e w25qxx_erase_sector(uint32_t sector_addr)
{
    uint8_t cmd[4];
    
    w25qxx_write_enable();
    
    cmd[0] = W25QXX_CMD_SECTOR_ERASE;
    cmd[1] = (sector_addr >> 16) & 0xFF;
    cmd[2] = (sector_addr >> 8) & 0xFF;
    cmd[3] = sector_addr & 0xFF;
    
    W25QXX_CS_LOW();
    w25qxx_spi_transmit(cmd, 4);
    W25QXX_CS_HIGH();
    
    w25qxx_wait_busy();
    
    log_d("擦除扇区 地址=0x%06X", sector_addr);
    return W25QXX_OK;
}

/**
 * @brief       擦除整个芯片
 *
 * @retval      W25QXX_Error_e: 错误码
 */
W25QXX_Error_e w25qxx_erase_chip(void)
{
    uint8_t cmd = W25QXX_CMD_CHIP_ERASE;
    
    w25qxx_write_enable();
    
    W25QXX_CS_LOW();
    w25qxx_spi_transmit(&cmd, 1);
    W25QXX_CS_HIGH();
    
    log_w("擦除整个芯片...");
    w25qxx_wait_busy();
    log_i("芯片擦除完成");
    
    return W25QXX_OK;
}

/**
 * @brief       检查芯片是否忙
 *
 * @retval      bool: true=忙, false=空闲
 */
bool w25qxx_is_busy(void)
{
    uint8_t status = w25qxx_read_status_reg(1);
    return (status & 0x01) ? true : false;
}

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       SPI发送数据
 */
static W25QXX_Error_e w25qxx_spi_transmit(const uint8_t *tx_data, uint16_t len)
{
#if (W25QXX_USE_BSP_SPI == 1)
    return (spi_write(tx_data, len) == SPI_OK) ? W25QXX_OK : W25QXX_ERROR;
#else
    if (HAL_SPI_Transmit(W25QXX_SPI_HANDLE, (uint8_t*)tx_data, len, W25QXX_TIMEOUT_MS) == HAL_OK)
    {
        return W25QXX_OK;
    }
    return W25QXX_ERROR;
#endif
}

/**
 * @brief       SPI接收数据
 */
static W25QXX_Error_e w25qxx_spi_receive(uint8_t *rx_data, uint16_t len)
{
#if (W25QXX_USE_BSP_SPI == 1)
    return (spi_read(rx_data, len) == SPI_OK) ? W25QXX_OK : W25QXX_ERROR;
#else
    if (HAL_SPI_Receive(W25QXX_SPI_HANDLE, rx_data, len, W25QXX_TIMEOUT_MS) == HAL_OK)
    {
        return W25QXX_OK;
    }
    return W25QXX_ERROR;
#endif
}

/**
 * @brief       写使能
 */
static W25QXX_Error_e w25qxx_write_enable(void)
{
    uint8_t cmd = W25QXX_CMD_WRITE_ENABLE;
    
    W25QXX_CS_LOW();
    w25qxx_spi_transmit(&cmd, 1);
    W25QXX_CS_HIGH();
    
    return W25QXX_OK;
}

/**
 * @brief       等待芯片就绪
 */
static W25QXX_Error_e w25qxx_wait_busy(void)
{
    uint32_t timeout = W25QXX_TIMEOUT_MS;
    
    while (w25qxx_is_busy() && timeout--)
    {
        HAL_Delay(1);
    }
    
    return (timeout > 0) ? W25QXX_OK : W25QXX_ERROR_TIMEOUT;
}

/**
 * @brief       读取状态寄存器
 */
static uint8_t w25qxx_read_status_reg(uint8_t reg_num)
{
    uint8_t cmd, status;
    
    cmd = (reg_num == 1) ? W25QXX_CMD_READ_STATUS_REG1 : W25QXX_CMD_READ_STATUS_REG2;
    
    W25QXX_CS_LOW();
    w25qxx_spi_transmit(&cmd, 1);
    w25qxx_spi_receive(&status, 1);
    W25QXX_CS_HIGH();
    
    return status;
}
